package org.flexthinker.binding.value.support
{
    import org.flexthinker.binding.value.PropertyChangeListener;
    import org.flexthinker.binding.value.PropertyChangePublisher;

    import flash.events.Event;

    import flash.utils.Dictionary;

    import mx.binding.utils.ChangeWatcher;
    import mx.events.PropertyChangeEvent;

    /**
     *  Wrapper for ChangeWatacher calling
     * <code>PropertyChangeListener.propertyChange(PropertyChangeEvent)</code> method
     *  when the observed value changes.
     *
     * Specific to this class is that it always calls listener w/ PropertyChangeEvent,
     * while ChangeWatcher doesn't do that always due to Binding with CustomEvent
     *
     */
    public class PropertyChangePublisherImpl implements PropertyChangePublisher
    {
        public var changeWatcher:ChangeWatcher;

        protected var host:Object;

        protected var propertyName:String;

        private var listeners:Vector.<PropertyChangeListener> = new Vector.<PropertyChangeListener>();


        public function PropertyChangePublisherImpl( host:Object, propertyName:String )
        {
            this.host = host;
            this.propertyName = propertyName;
        }

        public function addPropertyChangeListener( listener:PropertyChangeListener ):void
        {
            if( listener == null || host == null || propertyName == null)
            {
                return;
            }

            listeners.push( listener );

            setChangeWatcher();
        }

        public function removePropertyChangeListener( listener:PropertyChangeListener ):void
        {
            var idx:int = listeners.indexOf( listener );
            if ( idx >= 0 )
            {
                listeners.splice(idx,1);
            }
            if ( listeners.length == 0 )
            {
                unwatch();
            }
        }

        public function removeAllListeners():void
        {
            listeners = new Vector.<PropertyChangeListener>();
            unwatch();
        }

        public function get isListening():Boolean 
        {
            return ( changeWatcher != null && changeWatcher.isWatching() )
        }

        private function setChangeWatcher():void
        {
            if( changeWatcher == null )
            {
                changeWatcher = ChangeWatcher.watch( host, propertyName, propertyChangedHandler, false, false );
            }
        }

        /**
         * Called when all listeners have been removed
         */
        private function unwatch():void
        {
            if ( changeWatcher )
            {
                changeWatcher.unwatch();
                changeWatcher = null;
            }
        }

        private function propertyChangedHandler( event:Event ):void
        {
            var newEvt:PropertyChangeEvent = (event is PropertyChangeEvent ? event :
                                              new PropertyChangeEvent( PropertyChangeEvent.PROPERTY_CHANGE, false, false, null,
                                                                       propertyName, null, host[propertyName], host ));
            notifyListeners( newEvt );
        }

        private function notifyListeners(event:PropertyChangeEvent):void
        {
            for each ( var listener:PropertyChangeListener in listeners )
            {
                listener.propertyChange( event );
            }
        }


    }
}